﻿
Optimizer= {};

Optimizer._frame = nil;

Optimizer._recording = false;

Optimizer._timeRecStart = 0;
Optimizer._timeRecStart1970 = 0; 

Optimizer._playerName = "";

--WNS == playerNotSight
--WNR == playerNotRange
Optimizer._timeWNSstart = 0;
Optimizer._timeWNRstart = 0;
Optimizer._timeWNStotal = 0; -- Total time the Player received Not in Sight error
Optimizer._timeWNRtotal = 0; -- Total time the Player received Not in Range error

Optimizer._playerInSight = true;
Optimizer._playerInRange = true;

Optimizer._UpdateShedule = {};
Optimizer.nextSheduleTime = nil;
Optimizer.nextSheduleSpell = nil;

Optimizer.Player = nil;
Optimizer.myLog = nil;

Optimizer.regEvents = {
    ["SPELL_DAMAGE"] = {},
    ["SPELL_MISSED"] = {},
    ["SPELL_CAST_FAILED"] = {},
    ["SPELL_INTERRUPT"] = {},
    ["SPELL_CAST_START"] = {},
    ["SPELL_CAST_SUCCESS"] = {},
    ["UPDATE"] = {}
};

-----------------------------------
--      FUNCTIONS
-----------------------------------

function Optimizer:StartRecording()
    Optimizer._errors = nil;
    Optimizer._errors = {};
    Optimizer._recording = true;
    
    Optimizer._timeRecStart = GetTime(); 
    -- timeRecStart1970 - seconds since 1.1.1970. some api functions from wow with 
    -- lower resolution only deliver this.
    Optimizer._timeRecStart1970 = time();
    
    Optimizer._timeWNSstart = 0;
    Optimizer._timeWNRstart = 0;
    Optimizer._timeWNStotal = 0; -- Total time the Player received Not in Sight error
    Optimizer._timeWNRtotal = 0; -- Total time the Player received Not in Range error 
    
    Optimizer._playerInSight = true;
    Optimizer._playerInRange = true;

    Optimizer.Player:onCombat(Optimizer._timeRecStart);
    Optimizer.myLog:NewSegment(false,Optimizer._timeRecStart);
        
end

-- sorting function for the shedule
function Optimizer.CompShedule(a,b)
   i1,v1 = next(a);
   i2,v2 = next(b);
   if i1 > i2 then
      return true;
   else
      return false;
   end
end

function Optimizer:EndRecording()
    Optimizer.myLog:CloseSegment(GetTime());    
    Optimizer.myLog:PrintSegment(Optimizer.myLog.Segments[#Optimizer.myLog.Segments]);
    
    Optimizer._recording = false;     
    Optimizer._UpdateShedule = {};
    
    Optimizer.nextSheduleTime = nil;
    Optimizer.nextSheduleSpell = nil;    
end


function Optimizer.regSpell(event,name,func)
-- no nil verification atm, could need more combatlog event types in the future
--~     if Optimizer.regEvents[event] ~= nil then         
        Optimizer.regEvents[event][name] = func;
--~     end
end

function Optimizer:OnUIerror(...)
    if arg1 == ERR_NO_ATTACK_TARGET then
        Optimizer:OnUIerror_noTarget(); 
    end
end

function Optimizer:OnUIerror_noTarget()
    
end

function Optimizer:UpdateSightRange(time)

    if Optimizer._playerInSight == true then
        if Optimizer._timeWNSstart ~= 0 then
            Optimizer._timeWNStotal = Optimizer._timeWNStotal + (time-Optimizer._timeWNSstart);
            Optimizer._timeWNSstart = 0;
        end
    elseif Optimizer._playerInSight == false then
         if Optimizer._timeWNSstart == 0 then
--~             DEFAULT_CHAT_FRAME:AddMessage("out sight"); 
            Optimizer._timeWNSstart = time;        
        end
    end
    
    if Optimizer._playerInRange == true then
        if Optimizer._timeWNRstart ~= 0 then
            Optimizer._timeWNRtotal = Optimizer._timeWNRtotal + (time-Optimizer._timeWNRstart);
            Optimizer._timeWNRstart = 0;
        end
    elseif Optimizer._playerInRange == false then
        if Optimizer._timeWNRstart == 0 then
--~             DEFAULT_CHAT_FRAME:AddMessage("out range");
            Optimizer._timeWNRstart = time;               
        end    
    end      
end

function Optimizer:GetBookId(name)
    local i = 1
    local foundNum = nil;
    while true do
       local spellName, spellRank = GetSpellName(i, BOOKTYPE_SPELL)
        if not spellName then
          do break end
        end
        if name == spellName then
            foundNum = i;
            do break end
        end
        i = i + 1
    end

    return foundNum;
end

-----------------------------------
--      MAIN FRAME
-----------------------------------

function Optimizer:init()
    _events = CreateFrame("Frame");
    _events:Hide();

    _events:SetScript(
        "OnEvent",
        function(frame, event, ...)
            -- only trigger the UPDATE function when its needed
            -- without sheduler we waste loads of cpu time when the log gets busy
            if Optimizer._UpdateShedule[#Optimizer._UpdateShedule] ~= nil then
                -- dont waste time getting values, cache em
                if Optimizer.nextSheduleTime == nil then
                    Optimizer.nextSheduleTime,Optimizer.nextSheduleSpell = next(Optimizer._UpdateShedule[#Optimizer._UpdateShedule]);
--~                     DEFAULT_CHAT_FRAME:AddMessage(GetTime().." :: "..Optimizer.nextSheduleTime);
                end

                if GetTime() >= Optimizer.nextSheduleTime then
                    Optimizer.regEvents["UPDATE"][Optimizer.nextSheduleSpell](Optimizer.Player.spellLib[Optimizer.nextSheduleSpell],...); 
                    table.remove(Optimizer._UpdateShedule);
                    Optimizer.nextSheduleTime = nil;
                    Optimizer.nextSheduleSpell = nil;
                end
            end
            
            if event == "PLAYER_REGEN_DISABLED" then
                Optimizer:StartRecording();
            elseif event == "PLAYER_REGEN_ENABLED" then
                Optimizer:EndRecording();
            elseif event == "UI_ERROR_MESSAGE" then
                if arg1 ~= nil then
                --TODO: verify that these two error string from the globalstrings.lua are consistent trough all locales/spells
                    if arg1 == ERR_BADATTACKFACING then
                        Optimizer._playerInSight = false;
                    elseif arg1 == ERR_BADATTACKPOS then
                        Optimizer._playerInRange = false;
                    else
                    end
                    Optimizer:OnUIerror(...);
                end
            elseif event == "COMBAT_LOG_EVENT_UNFILTERED" then
                -- filter out all messages that are not related to the player
                if arg2 ~= nil and arg4 == UnitName("player") then
--~                     DEFAULT_CHAT_FRAME:AddMessage(arg2.." : "..arg10);
                    -- double if cause we cant catch the nil then
                    if Optimizer.regEvents[arg2] ~= nil then
                        if Optimizer.regEvents[arg2][arg10] ~= nil then
                            Optimizer.regEvents[arg2][arg10](Optimizer.Player.spellLib[arg10],...);  
                        end
                    end 
                    
                    if arg2 == "SWING_DAMAGE" then
                        Optimizer._playerInSight = true;
                        Optimizer._playerInRange = true;
                    elseif arg2 == "SWING_MISS" then
                        Optimizer._playerInSight = true;
                        Optimizer._playerInRange = true;
                    end
                end
            else
            end
            Optimizer:UpdateSightRange(GetTime());
        end
    );

    _events:RegisterEvent("PLAYER_REGEN_DISABLED");
    _events:RegisterEvent("PLAYER_REGEN_ENABLED");
    _events:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED");
    _events:RegisterEvent("UI_ERROR_MESSAGE");
    
    local unit = "player";
    if unit == "player" then
      if UnitClass(unit) == "Kriegerin" or UnitClass(unit) == "Krieger" then
            Optimizer.Player = Optimizer.Class.Warrior:new(); 
            Optimizer.Player:initLib();
            Optimizer.Player:registerSpells();
      end
   else
      -- NETSI observer mode init here
   end

    Optimizer.myLog = Optimizer.Log:new();
   
end

-- Set up a useless frame to handle events
Optimizer._frame = CreateFrame("Frame");
Optimizer._frame:Hide();
Optimizer._frame:SetScript("OnEvent", function()
        if event == "PLAYER_LOGIN" then
        if UnitClass("player") == "Kriegerin" or UnitClass("player") == "Krieger" then
            Optimizer:init();
        end
        end    
    end
);

Optimizer._frame:RegisterEvent("PLAYER_LOGIN");
--[[
if Optimizer._errors[arg12..'spell'] == nil then
    Optimizer._errors[arg12..'spell'] = 0;
end
Optimizer._errors[arg12..'spell'] = Optimizer._errors[arg12..'spell'] + 1;

for i=1,select("#", ...) do
    local x = select(i, ...)
    DEFAULT_CHAT_FRAME:AddMessage('arg'..i..': '..x);
end
]]--

-- taken directly from http://lua-users.org/wiki/InheritanceTutorial
-- Create a new class that inherits from a base class
function Optimizer.inheritsFrom( baseClass )

    -- Create the table and metatable representing the class.
    local new_class = {}
    local class_mt = { __index = new_class }

    -- Note that this function uses class_mt as an upvalue, so every instance
    -- of the class will share the same metatable.
    --
    function new_class:new()
        local newinst = {}
        setmetatable( newinst, class_mt )
        return newinst
    end

    -- The following is the key to implementing inheritance:

    -- The __index member of the new class's metatable references the
    -- base class.  This implies that all methods of the base class will
    -- be exposed to the sub-class, and that the sub-class can override
    -- any of these methods.
    --
    if baseClass then
        setmetatable( new_class, { __index = baseClass } )
    end

    return new_class
end